package tcpsocket

import (
	"bufio"
	"com/mixmedia/utils/SyncMap"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
)

//定义日志类
var log = NewLogging()

type tcpserver struct {
	readQuene, writeQuene      chan *BufferEvent
	host, port                 string
	connection_maps            *SyncMap.TCPConn
	connection_write_chan_maps *ChanBufferEvent
	handler                    EventHandler
	debug                      bool
	lock                       *sync.Mutex
}

type BufferEvent struct {
	addr, buffer string
}

func (s *tcpserver) Start(handler EventHandler) (err error) {
	log.Info("Initiating server... (Ctrl-C to stop)")

	remote := s.host + ":" + s.port
	addr, err1 := net.ResolveTCPAddr("tcp", remote)
	if err1 != nil {
		log.Error("Error creating listener: %s", err)
		return
	}
	listener, err := net.ListenTCP("tcp", addr)

	if err != nil {
		log.Error("Error creating listener: %s", err)
		return
	}
	s.handler = handler
	s.handler.RegisterServer(s)
	s.process(listener)
	return
}

func (s *tcpserver) SetDebug(status bool) {
	s.debug = status
	SetLogLevel(s.debug)
}

func (s *tcpserver) dispose() {
	close(s.writeQuene)
	close(s.readQuene)
}

func (s *tcpserver) process(_listener *net.TCPListener) {
	defer _listener.Close()
	defer s.dispose()
	go s.handleRead()
	go s.handleWrite()
	for {
		conn, error := _listener.AcceptTCP()
		if error != nil {
			log.Error("Error: Accepting data: %s", error)
			continue
		}
		err := conn.SetKeepAlive(true)
		if err != nil {
			log.Error("Error:%s on SetKeepAlive addr: %s", err, conn.RemoteAddr())
		}
		log.Debug("Accept a new connection: %s", conn.RemoteAddr())
		connkey := fmt.Sprintf("%s", conn.RemoteAddr())
		s.connection_maps.Set(connkey, conn)
		write_chan := make(chan *BufferEvent)
		s.connection_write_chan_maps.Set(connkey, write_chan)
		go s.handleConn(conn)
	}
}

func (s *tcpserver) handleRead() {
	for {
		buffer, status := <-s.readQuene
		if !status {
			break
		}
		_, status = s.connection_maps.Get(buffer.addr)
		if !status {
			continue
		}
		log.Debug("get request of %s: %s", buffer.addr, buffer.buffer)
		s.handler.HandleReade(buffer.addr, buffer.buffer)
	}
}

func (s *tcpserver) writeToConn(buf *BufferEvent) (result bool, err error) {
	conn, status := s.connection_maps.Get(buf.addr)
	if !status {
		result = false
		err = errors.New(fmt.Sprintf("address %s not exist.", buf.addr))
		log.Error("Error: Writing data : %s", err)
		return
	}
	//throw timeout error when wait for 10s
	//conn.SetWriteDeadline(time.Now().Add(10 * time.Second))

	writer := bufio.NewWriter(conn)
	bufferString := s.handler.HandleWrite(buf.addr, buf.buffer)
	_, err = writer.WriteString(bufferString)
	if err != nil {
		log.Error("Error: %s Writing data : %s", buf.addr, err)
		s.handler.HandleError(buf.addr, err)
		result = false
		return
	}
	err = writer.Flush()
	if err != nil {
		log.Error("Error: %s Writing data : %s", buf.addr, err)
		s.handler.HandleError(buf.addr, err)
		result = false
		return
	}
	result = true
	return
}

func (s *tcpserver) handleChanWrite(connKey string) {
	defer func() {
		conn, stat := s.connection_maps.Get(connKey)
		if !stat {
			return
		}
		go s.closeConn(conn)
	}()
	connWChan, st := s.connection_write_chan_maps.Get(connKey)
	if !st {
		return
	}
	for {
		buffer, status := <-connWChan
		if !status {
			break
		}
		_, err := s.writeToConn(buffer)
		if err != nil {
			s.handler.HandleError(connKey, err)
			break
		}
	}
}

func (s *tcpserver) handleWrite() {
	defer func() {
		if r := recover(); r != nil {
			log.Error("Error: recover:%s", r)
			go s.handleWrite()
		}
	}()
	for {
		buffer, status := <-s.writeQuene
		if !status {
			break
		}
		connWChan, st := s.connection_write_chan_maps.Get(buffer.addr)
		if !st {
			continue
		}
		connWChan <- buffer
	}
}

func (s *tcpserver) Write(connkey string, content string) {
	buffer := BufferEvent{
		addr:   connkey,
		buffer: content,
	}
	s.writeQuene <- &buffer
}

func (s *tcpserver) Close(connkey string) {
	conn, status := s.connection_maps.Get(connkey)
	if !status {
		return
	}
	go s.closeConn(conn, connkey)
}

func (s *tcpserver) handleConn(conn *net.TCPConn) {
	defer s.closeConn(conn)
	defer func() {
		if r := recover(); r != nil {
			log.Error("Error: Recovered in :%s", r)
			go s.handleConn(conn)
		}
	}()

	connkey := fmt.Sprintf("%s", conn.RemoteAddr())
	s.handler.HandleConnect(connkey)
	go s.handleChanWrite(connkey)
	data := make([]byte, 1024)
	for {
		n, error := conn.Read(data)
		if error != nil {
			log.Error("Error: Reading data : %s", error)
			s.handler.HandleError(connkey, error)
			break
		}
		buffer := BufferEvent{
			addr:   connkey,
			buffer: string(data[0:n]),
		}
		s.readQuene <- &buffer
	}
}

func (s *tcpserver) closeConn(conn *net.TCPConn, connkeys ...string) {
	if s.connection_maps == nil || len(s.connection_maps.Items()) == 0 {
		return
	}
	s.lock.Lock()
	defer s.lock.Unlock()
	key := fmt.Sprintf("%s", conn.RemoteAddr())
	writeChan, st := s.connection_write_chan_maps.Get(key)
	if st {
		s.connection_write_chan_maps.Delete(key)
		close(writeChan)
	}
	s.connection_maps.Delete(key)
	if len(connkeys) > 0 && strings.EqualFold(key, connkeys[0]) {
		log.Info("callback loop! addr:%s", key)
	} else {
		s.handler.HandleDisConnect(key)
	}
	conn.Close()

	if s.debug {
		log.Debug("client %s disconnect", key)
	}
}
